
#ifndef __WIN32__
#include <unistd.h>
#include <apr_signal.h>
#endif

#include "iface/APRInterface.h"
#include "iface/CryptInterface.h"
#include "iface/SQLiteInterface.h"
#include "iface/XMLInterface.h"

#include "net/Broker4Server.h"

#include "utils/BaseConvert.h"
#include "utils/StringUtils.h"

#include "default_hosts.h"
#include "acquire_config.h"

#define KEYONSIGNAL SIGINT

// Want to catch SIGSEGV to write back databases
// Want to use memory marking from valgrind to be pedantic about user data

int allstop;

#ifndef __WIN32__
void signal_setflag(int signo)
{
   printf("Received signal (%d ? %d)\n", signo, KEYONSIGNAL);
   if (signo == KEYONSIGNAL)
      allstop = 1;
}
#endif

int main(int argc, const char *argv[])
{
   int nargc = argc;
   const char *const *nargv = argv;
   APRInterface apr_iface(&nargc, &nargv);
   SSLInterface ssl_iface;
   CryptInterface crypt_iface;
   SQLiteInterface sqlite_iface;
   XMLInterface xml_iface;
   allstop = 0;
   //DBArbitrator::prereadKeyDB(ACQUIRE_STATIC_DIR "/brokerkey", ACQUIRE_STATIC_DIR "/brokerkeyname", "broker");
#ifndef __WIN32__
//   apr_signal_block(SIGINT);
//   apr_signal_block(SIGTERM);
//   apr_sigfunc_t *sighandler = apr_signal(SIGTERM, &signal_setflag);
   apr_sigfunc_t *sighandler = apr_signal(KEYONSIGNAL, &signal_setflag);
#endif
   // numbers of threads, port number, host address (and so interface), higher memory pool
   char *wip = KeyDB::AskPassword("Please type the Work-In-Progress (WIP) password: ");
   KeyDB *keydb = new KeyDB(ACQUIRE_DYNAMIC_DIR "/" "broker_keydb", wip, BROKERNAME, NULL, 1);
   free(wip);
   if (!(keydb->HasOwnKey()))
   {
      delete keydb;
      return 0;
   }
   keydb->EnsureChainBaseAnchored(keydb->selfkey);
   keydb->OutputNamedPublicKeyToFile(BROKERNAME, ACQUIRE_DYNAMIC_DIR "/hosted_files/broker.pub", apr_iface.mp);
   char *gotlatestkeyname = keydb->GetLatestPrivateKeyNameOfOwner(BROKERNAME);
   StringUtils::WriteStringToFile(gotlatestkeyname, ACQUIRE_DYNAMIC_DIR "/hosted_files/brokerkeyname", apr_iface.mp);
   char *gotlatestkeystore = StringUtils::varprintf(ACQUIRE_DYNAMIC_DIR "/hosted_files/%s.pub", gotlatestkeyname);
   keydb->OutputNamedPublicKeyToFile(gotlatestkeyname, gotlatestkeystore, apr_iface.mp);
   free(gotlatestkeyname);
   free(gotlatestkeystore);
   
   PendingDB *pendingdb = new PendingDB(ACQUIRE_DYNAMIC_DIR "/" "broker_pending");
   ActorsDB *actorsdb = new ActorsDB(ACQUIRE_DYNAMIC_DIR "/" "broker_actors");
   ClusterSSHDB *clusterdb = new ClusterSSHDB(ACQUIRE_DYNAMIC_DIR "/" "broker_clusterdb");
   KeyDB *sshkeydb = new KeyDB(ACQUIRE_DYNAMIC_DIR "/" "broker_sshkeys", NULL, NULL, NULL, 1);

   AllDBs *alldb = new AllDBs();
   alldb->SetKeyDB(keydb);
   alldb->SetPendingDB(pendingdb);
   alldb->SetActorsDB(actorsdb);
   alldb->SetSSHKeyDB(sshkeydb);
   alldb->SetClusterSSHDB(clusterdb);
   clusterdb->MergeXMLWithClusterDB(ACQUIRE_STATIC_DIR "/" "clusters.xml");
   
   printf("Starting broker server...\n");
   // Only 4 threads?
   Broker4Server *tcpserv = new Broker4Server(32, 10000, "0.0.0.0", apr_iface.mp);
   tcpserv->dbs = alldb;
   tcpserv->StartSessionThreads();
   tcpserv->StartListeningThread();
   printf("Housekeeping thread active...\n");
   uint64_t heartbeats = 0;
   while (!allstop)
   {
#ifndef __WIN32__
      sleep(1);
#endif
      // I know this shouldn't be necessary, but I want to make sure
      // that the threads cannot hang/lock/wait forever.
      for (int i = 0; i < tcpserv->noofthreads; i++)
         tcpserv->pipearray[i].waiting->signal();
      // Initiate compute session database clean up every minute - may want to stagger these later
      if ((heartbeats % 60) == 0) DBArbitrator::CleanExpiredComputeSessions(alldb);
      if ((heartbeats % 600) == 0) alldb->actorsdb->CleanExpiredWork();
      /*
      if ((heartbeats % 3600) == 0)
      {
         // Try to update keys if they have expired (expire 86400 seconds after creation)
         alldb->keydb->TryToUpdateOwnersKeys(BROKERNAME, 86400);
         // Would add the delegate key to the actors DB so that it can be trusted, but the brokerkey is actually fairly mundane and doesn't need this
      }
      */
      if ((heartbeats % 5) == 0)
      {
         // Try to update keys if they have expired (expire 86400 seconds after creation)
         char *prevkeysname = alldb->keydb->GetLatestPrivateKeyNameOfOwner(BROKERNAME);
         if (alldb->keydb->TryToUpdateOwnersKeys(alldb->sshkeydb, BROKERNAME, 60))
         {
            apr_pool_t *hmp;
            apr_pool_create(&hmp, apr_iface.mp);
            char *ownerkeysname = alldb->keydb->GetLatestPrivateKeyNameOfOwner(BROKERNAME);
            char *gotprevkeystore = StringUtils::varprintf(ACQUIRE_DYNAMIC_DIR "/hosted_files/%s.pub", prevkeysname);
            apr_status_t status;
            if (strcmp(prevkeysname, BROKERNAME) != 0)
            {
               //printf("'%s' '%s'\n", prevkeysname, BROKERNAME);
               status = apr_file_remove(StringUtils::expandMyHomeDirectoryTilde(gotprevkeystore, hmp), hmp);
               CheckAPRError(status);
            }
            free(gotprevkeystore);
            StringUtils::WriteStringToFile(ownerkeysname, ACQUIRE_DYNAMIC_DIR "/hosted_files/brokerkeyname", hmp);
            char *gotlatestkeystore = StringUtils::varprintf(ACQUIRE_DYNAMIC_DIR "/hosted_files/%s.pub", ownerkeysname);
            keydb->OutputNamedPublicKeyToFile(ownerkeysname, gotlatestkeystore, hmp);
            free(gotlatestkeystore);
            printf("NEW OWNER KEY NAME: %s\n", ownerkeysname);
            free(ownerkeysname);
            apr_pool_destroy(hmp);
         }
         free(prevkeysname);
         // Would add the delegate key to the actors DB so that it can be trusted, but the brokerkey is actually fairly mundane and doesn't need this
      }
      if ((heartbeats % (24*60*60)) == 3)
      {
         //printf("\nKEYDB:\n");
         keydb->CleanOldKeys();
         //sprintf("\nSSHKEYDB:\n");
         sshkeydb->CleanOldKeys();
      }
      if ((heartbeats % 5) == 4)
      {
         apr_status_t status;
         apr_pool_t *hmp;
         status = apr_pool_create(&hmp, apr_iface.mp);
         CheckAPRError(status);
         // This needs the remote-contactable address and port of the broker to start compute
         Broker4::TryToStartUnresourcedWork(alldb, DEFAULT_HOST, DEFAULT_PORT, hmp);
         apr_pool_destroy(hmp);
      }
      if ((heartbeats % 7500) < 10)
      {
         if ((heartbeats % 7500) == 0) alldb->actorsdb->Backup();
         if ((heartbeats % 7500) == 1) alldb->keydb->Backup();
         if ((heartbeats % 7500) == 2) alldb->sshkeydb->Backup();
         if ((heartbeats % 7500) == 3) alldb->clustersdb->Backup();
         if ((heartbeats % 7500) == 4) alldb->pendingdb->Backup();
      }
      // When key cleanup happens here, should make it change the letter on the first pass, and delete on the second pass!
      heartbeats++;
   }
   printf("Total heartbeats - %" APR_SIZE_T_FMT "\n", heartbeats);
   printf("Shutting down...\n");
   delete tcpserv;
   printf("Closed connections...\n");
   printf("Writing back databases...\n");
   delete alldb;
   return 0;
}
